/* Copyright ©, 2023, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary
 * to ECARX (Hubei) Technology Co., Ltd. and/or other developers. No license to
 * any intellectual property rights contained within this file is either granted
 * or implied except as may be provided in an agreement with ECARX (Hubei)
 * Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole
 * or in part is prohibited.
 */

#define LOG_TAG "PKIHal"

// #include "PKIGrpcServer.h"
#include "PKIHal.h"
#include "aes/checksum.h"
#include "android-base/properties.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "hidl/HidlSupport.h"
#include "log/log_main.h"
#include "vendor/ecarx/xma/pki/1.0/types.h"
#include "secure_storage_sdk.h"
#include <fstream>
#include <memory>
#include <string>
#include <sys/types.h>
#include <vector>

#define DEVICE_NAME "/dev/block/sda11"
#define PKI_BLOCK_START 0 * 512
#define PASSWORD_BLOCK_START 13 * 512

namespace vendor::ecarx::xma::pki::V1_0::implementation {

static int bytesToInt(uint8_t *bytes) {
  int data = bytes[3];
  data |= (bytes[2] << 8);
  data |= (bytes[1] << 16);
  data |= (bytes[0] << 24);
  return data;
}

struct PKIHalPassthrough : public PKIHal {

  PKIHalPassthrough();
  ~PKIHalPassthrough();

  Certification readCertificate(StatusCode *outStatus) override;
  Certification readSWK3Certificate(const android::hardware::hidl_string& filename, StatusCode *outStatus) override;

  StatusCode updatePKIInstallStatus(PKIInstallPlatform clientId,
                                    InstallStatus outStatus) override;
  StatusCode
  writeBlock(int32_t offset,
             const android::hardware::hidl_vec<int8_t> &bytesData) override;
  std::vector<uint8_t> readSecurityInfoChecksum() override;

private:
  // void writeSWK1K2toSystemProperty();
  void clearCertificateAndPassword();
  int readSecureStorage(const android::hardware::hidl_string& filename, int domid, int domuid, unsigned char*& data);

private:
  InstallStatus slaveInstalled{InstallStatus::UNKNOW};
  InstallStatus masterInstalled{InstallStatus::UNKNOW};
  // std::unique_ptr<PKIGrpcServer> mGrpcServer;
};

PKIHalPassthrough::PKIHalPassthrough() {
  ALOGV("%s in", __FUNCTION__);
  // mGrpcServer = makeGRPCServer();
  // mGrpcServer->offer(this);
  // writeSWK1K2toSystemProperty();
}

PKIHalPassthrough::~PKIHalPassthrough() {}

Certification PKIHalPassthrough::readCertificate(StatusCode *outStatus)
{
  ALOGD("%s in", __FUNCTION__);
  android::hardware::hidl_string swk1("SWK1");
  android::hardware::hidl_string swk2("SWK2");
  Certification cert;
  int swk1Len = -1;
  int swk2Len = -1;
  unsigned char *swk1data = nullptr;
  unsigned char *swk2data = nullptr;
  cert.status = CertStatus::UNAVAILABLE;
  (*outStatus) = StatusCode::INTERNAL_ERROR;

  swk1Len = readSecureStorage(swk1, RECOVER, 0, swk1data);
  if (swk1Len < 0 || !swk1data) {
    return cert;
  }
  swk2Len = readSecureStorage(swk2, RECOVER, 0, swk2data);
  if (swk2Len < 0 || !swk2data) {
    if (swk1data) delete[] swk1data;
    if (swk2data) delete[] swk2data;
    return cert;
  }
 
  ALOGD("read %s and %s success\n", swk1.c_str(), swk2.c_str());
  cert.rawValue.setToExternal(swk1data, swk1Len, true);//delete by cert.rawValue
  cert.password = android::hardware::hidl_string((char *)swk2data);
  cert.status = CertStatus::AVAILABLE;
  (*outStatus) = StatusCode::OK;
  delete[] swk2data;
 
  return cert;
}

StatusCode
PKIHalPassthrough::updatePKIInstallStatus(PKIInstallPlatform clientId,
                                          InstallStatus status) {
  ALOGD("%s: clientId %s status %s", __func__, toString(clientId).c_str(),
        toString(status).c_str());
  if (clientId == PKIInstallPlatform::SLAVE_AP) {
    slaveInstalled = status;
  } else if (clientId == PKIInstallPlatform::MASTER_AP) {
    masterInstalled = status;
  }
  if (slaveInstalled == InstallStatus::SUCCESS &&
      masterInstalled == InstallStatus::SUCCESS) {
    ALOGD("%s: clear pki block data", __func__);
    clearCertificateAndPassword();
  }
  if ((slaveInstalled == InstallStatus::SUCCESS ||
       slaveInstalled == InstallStatus::HAS_INSTALLED) &&
      (masterInstalled == InstallStatus::SUCCESS ||
       masterInstalled == InstallStatus::HAS_INSTALLED)) {
    // todo 上报DID
    ALOGD("%s: report did", __func__);
  }
  return StatusCode::OK;
}

void PKIHalPassthrough::clearCertificateAndPassword() {
  FILE *file = nullptr;
  if ((file = fopen(DEVICE_NAME, "rb+")) != nullptr) {
    fseek(file, 0, SEEK_SET);
    uint8_t lengthData[4];
    fread(lengthData, 4, 1, file);
    int certificateLen = bytesToInt(lengthData) + 4;
    char *clearData = new char[certificateLen]{};
    fseek(file, 0, SEEK_SET);
    ALOGD("%s: clear cert, lenth %d", __func__, certificateLen);
    fwrite(clearData, certificateLen, 1, file);
    delete[] clearData;

    fseek(file, PASSWORD_BLOCK_START, SEEK_SET);
    fread(lengthData, 4, 1, file);
    int ihuPwdDataLenght = bytesToInt(lengthData) + 4;
    ALOGD("%s: clear password, lenth %d", __func__, ihuPwdDataLenght);
    fseek(file, PASSWORD_BLOCK_START, SEEK_SET);
    char *clearPsd = new char[ihuPwdDataLenght]{};
    fwrite(clearPsd, ihuPwdDataLenght, 1, file);
    delete[] clearPsd;
    fclose(file);
  } else {
    ALOGE("Cannot open %s", DEVICE_NAME);
  }
}

StatusCode PKIHalPassthrough::writeBlock(
    int32_t offset, const android::hardware::hidl_vec<int8_t> &bytesData) {
  FILE *file;
  if ((file = fopen(DEVICE_NAME, "rb+")) != nullptr) {
    fseek(file, offset, SEEK_SET);
    fwrite(bytesData.data(), bytesData.size(), 1, file);
    fflush(file);
    fclose(file);
    return StatusCode::OK;
  } else {
    ALOGE("Can not open %s", DEVICE_NAME);
    return StatusCode::INTERNAL_ERROR;
  }
}
#if 0
void PKIHalPassthrough::writeSWK1K2toSystemProperty() {
  std::ifstream ifPkiBlock;
  char invaildPreData[] = {'0', '0', '0', '0'};
  // PN的存储格式为：<4Bytes有效数据长度>＋<8Bytes吉利PN>＋<7Bytes沃尔沃PN>
  //  read SWK1 Info(eg: 8889978754A -> raw data:88 89 97 87 54 20 20 41 00 00
  //  00 00 00 00 00)
  // SWK1 的PN在PKI分区的第12个block
  char swkGeelyNumberBytes[5];
  char swkVolvoNumberBytes[4];
  char swkVersionBytes[3 + 1];
  memset(swkVersionBytes, 0, sizeof(swkVersionBytes));

  char prefix4Bytes[4]{0, 0, 0, 0};
  uint32_t len = 0;
  std::string strPartNumber;

  ifPkiBlock.open(DEVICE_NAME, std::ifstream::in);
  if (!ifPkiBlock.good()) {
    return;
  }
  ifPkiBlock.seekg(512 * 12);
  ifPkiBlock.read(prefix4Bytes, 4);

  len = prefix4Bytes[0];
  len = (len << 8) + prefix4Bytes[1];
  len = (len << 8) + prefix4Bytes[2];
  len = (len << 8) + prefix4Bytes[3];
  if (len >= 8) {
    ifPkiBlock.read(swkGeelyNumberBytes, 5);
    ifPkiBlock.read(swkVersionBytes, 3);

    strPartNumber = android::base::StringPrintf(
        "%02x%02x%02x%02x%02x", (int)swkGeelyNumberBytes[0],
        (int)swkGeelyNumberBytes[1], (int)swkGeelyNumberBytes[2],
        (int)swkGeelyNumberBytes[3], (int)swkGeelyNumberBytes[4]);
    strPartNumber.append(android::base::Trim(std::string(swkVersionBytes)));
    android::base::SetProperty("persist.pn.swk1.geely", strPartNumber.c_str());

    ALOGD("%s: SWK1 Geely PN: %s", __func__, strPartNumber.c_str());
  } else {
    android::base::SetProperty("persist.pn.swk1.geely", "");
  }

  if (len >= 15) {
    ifPkiBlock.read(swkVolvoNumberBytes, 4);
    ifPkiBlock.read(swkVersionBytes, 3);
    strPartNumber = android::base::StringPrintf(
        "%02x%02x%02x%02x", (int)swkVolvoNumberBytes[0],
        (int)swkVolvoNumberBytes[1], (int)swkVolvoNumberBytes[2],
        (int)swkVolvoNumberBytes[3]);
    strPartNumber.append(android::base::Trim(std::string(swkVersionBytes)));
    android::base::SetProperty("persist.pn.swk1.volvo", strPartNumber.c_str());

    ALOGD("%s: SWK1 Volvo PN: %s", __func__, strPartNumber.c_str());
  } else {
    android::base::SetProperty("persist.pn.swk1.volvo", "");
  }

  // SWK2的PN在PKI分区的第15个block
  ifPkiBlock.seekg(512 * 15, std::ios::beg);
  ifPkiBlock.read(prefix4Bytes, 4);

  len = prefix4Bytes[0];
  len = (len << 8) + prefix4Bytes[1];
  len = (len << 8) + prefix4Bytes[2];
  len = (len << 8) + prefix4Bytes[3];
  if (len >= 8) {
    ifPkiBlock.read(swkGeelyNumberBytes, 5);
    ifPkiBlock.read(swkVersionBytes, 3);

    strPartNumber = android::base::StringPrintf(
        "%02x%02x%02x%02x%02x", (int)swkGeelyNumberBytes[0],
        (int)swkGeelyNumberBytes[1], (int)swkGeelyNumberBytes[2],
        (int)swkGeelyNumberBytes[3], (int)swkGeelyNumberBytes[4]);
    strPartNumber.append(android::base::Trim(std::string(swkVersionBytes)));
    android::base::SetProperty("persist.pn.swk2.geely", strPartNumber.c_str());

    ALOGD("%s: SWK2 Geely PN: %s", __func__, strPartNumber.c_str());
  } else {
    android::base::SetProperty("persist.pn.swk2.geely", "");
  }

  if (len >= 15) {
    ifPkiBlock.read(swkVolvoNumberBytes, 4);
    ifPkiBlock.read(swkVersionBytes, 3);
    strPartNumber = android::base::StringPrintf(
        "%02x%02x%02x%02x", (int)swkVolvoNumberBytes[0],
        (int)swkVolvoNumberBytes[1], (int)swkVolvoNumberBytes[2],
        (int)swkVolvoNumberBytes[3]);
    strPartNumber.append(android::base::Trim(std::string(swkVersionBytes)));
    android::base::SetProperty("persist.pn.swk2.volvo", strPartNumber.c_str());

    ALOGD("%s: SWK2 Volvo PN: %s", __func__, strPartNumber.c_str());
  } else {
    android::base::SetProperty("persist.pn.swk2.volvo", "");
  }

  ifPkiBlock.close();
}
#endif
std::vector<uint8_t> PKIHalPassthrough::readSecurityInfoChecksum() {
  unsigned char checksum[32];
  std::vector<uint8_t> value;
  if (getCheckMemKeyHash(checksum)) {
    value.resize(32, 0);
    memcpy(value.data(), checksum, 32);
  }
  return value;
}

Certification PKIHalPassthrough::readSWK3Certificate(const android::hardware::hidl_string& filename, StatusCode *outStatus)
{
  Certification cert;
  int ret = -1;
  unsigned char *certData = nullptr;
  ALOGD("%s start read cert: %s", __func__, filename.c_str());
  cert.status = CertStatus::UNAVAILABLE;
  (*outStatus) = StatusCode::INTERNAL_ERROR;
#define DOM0UID 1009
  ret = readSecureStorage(filename, DOM0, DOM0UID, certData);
  if (ret >= 0 && certData) {
    ALOGD("read cert:%s success", filename.c_str());
    cert.rawValue.setToExternal(certData, ret, true);//delete by cert.rawValue
    (*outStatus) = StatusCode::OK;
    cert.status = CertStatus::AVAILABLE;
  }

  return cert;
}

int PKIHalPassthrough::readSecureStorage(
                  const android::hardware::hidl_string& filename,
                  int domid,
                  int domuid,
                  unsigned char*& data)
{
  int flag = SS_RD;
  int res = -1;
  int fd = 0;
  int dataLen = -1;
  ALOGD("%s start read secure storage: %s", __func__, filename.c_str());

  data = nullptr;
  res = secure_storage_open2(filename.c_str(), domid, domuid, flag);
  do {
    if (res > 0) {
      fd = res;
      dataLen = secure_storage_seek(fd, 0, SS_SEEK_END);
      if (dataLen < 0) {
        ALOGE("secure storage seek failure:%d", dataLen);
        break;
      }
      secure_storage_seek(fd, 0, SS_SEEK_SET);
      data = new unsigned char[dataLen];
      if (!data) {
        ALOGE("no memory!");
        dataLen = -1;
        break;
      }
      res = secure_storage_read(fd, data, dataLen);
      if (res >= 0) {
        ALOGD("read secure storage:%s success", filename.c_str());
      } else {
        dataLen = -1;
        ALOGE("secure_storage_read error (0x%x), read len:%d", res, dataLen);
      }
    } else {
      ALOGE("secure_storage_open2 error (0x%x)", res);
    }
  } while(0);
  if (fd > 0) secure_storage_close(fd);
  if (dataLen < 0 && data) {
    delete[] data;
    data = nullptr;
  }

  return dataLen;
}
#ifdef PKI_SERVER_HAL
std::unique_ptr<PKIHal> makePKIHal() {
  return std::make_unique<PKIHalPassthrough>();
}
#endif

} // namespace vendor::ecarx::xma::pki::V1_0::implementation
